# @Time : 2024/12/17 22:23
# @Author : Fioman 
# @Phone : 13149920693
"""
数据库的原始操作类,对数据库的底层封装
"""
import threading
import time
from datetime import datetime
from typing import Union, Dict, List

import pymongo

import app_config
from common.datetime_helper import get_now_str, get_shift_period
from common.exception_helper import error_handler_produce
from common.produce_logger import debug_produce
from db_tool.db_config import TableName, TableRegister
from db_tool.table_init import *
from personal_tool.data_result import DataResult


class DbHandler(object):
    """
    DbHandler封装的MongoDb接口,包括插入数据,更新数据等.
    """
    url = "mongodb://localhost:27017/"
    dbName = app_config.projectName
    _instance = None
    _lock = threading.Lock()

    def __new__(cls):
        if not cls._instance:
            with cls._lock:
                cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self):
        if not hasattr(self, "_initialized"):
            debug_produce("DbHandler.__init__() 进行初始化")
            self._initialized = True
            self.client = pymongo.MongoClient(DbHandler.url)
            self.paramsDb = self.client[self.dbName]
            init_tables()
            self.create_table_if_not_exist()

    def create_table_if_not_exist(self):
        """
        动态创建所有已注册的表
        所有的表的注册都在它们自己的文件中去执行,然后执行的方式,是在模块的__init__中去导入
        这样的好处就是后面每次创建表的时候,只需要在table_init()中添加一个文件即可,这里只需要去遍历
        所有的表明和初始化的文件即可.
        :return:
        """
        for tableName, initData in TableRegister.get_all_tables().items():
            self.init_data(tableName, initData)

    def insert_data(self, tableName: Union[str, TableName], data: Union[Dict, List[Dict]]) -> DataResult:
        """
         传入的数据的数据类型可能是列表,如果是列表,列表里面的每一项都是字典
         ,则认为是插入多条记录,否则就认为是插入一条记录
        @param tableName:可能是字符串,也可能是枚举类型(表明通过枚举,这样不用每次都书写字符串)
        @param data:
        @return:
        """
        # 如果是枚举类型,就转换成其属性的字符串
        if isinstance(tableName, TableName):
            tableName = tableName.name
        result = DataResult()
        try:
            if isinstance(data, list) and len(data) > 0 and isinstance(data[0], dict):
                self.paramsDb[tableName].insert_many(data)
                result.state = True
            elif isinstance(data, dict):
                self.paramsDb[tableName].insert_one(data)
                result.state = True
            else:
                result.info = ("in in DbHandler().insert_data() error: 插入的数据类型错误,"
                               "请传入字典或者是字典的列表")
        except Exception as e:
            result.info = str(e)
            debug_produce(f"in DbHandler().insert_data() error: {str(e)}")
            return result
        return result

    def update_data(self, tableName: Union[str, TableName], query: Dict,
                    newValues: Dict, updateOne: bool = False) -> DataResult:
        """
        更新数据,根据query筛选条件去更新数据
        @param tableName:表名
        @param query:筛选条件
        @param newValues:新的值
        @param updateOne:是否只更新一条数据的标志位,默认是False,更新全部
        @return:更新成功返回
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            if updateOne:
                self.paramsDb[tableName].update_one(query, newValues)
            else:
                self.paramsDb[tableName].update_many(query, {'$set': newValues})
            result.state = True
        except Exception as e:
            result.state = str(e)
            debug_produce(f"in DbHandler().update_data() error: {str(e)}")
            return result
        return result

    def delete_data(self, tableName: Union[str, TableName], query: Dict,
                    deleteOne: bool = False) -> DataResult:
        """
        删除数据,根据数据库表明和指定的条件去删除数据记录,可以指定删除一条,还是全部
        @param tableName: 表明
        @param query: 删除条件
        @param deleteOne: 是否只删除一条记录(如果有多条满足条件的记录的话)
        @return:
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            if deleteOne:
                self.paramsDb[tableName].delete_one(query)
            else:
                self.paramsDb[tableName].delete_many(query)
            result.state = True
        except Exception as e:
            result.info = str(e)
            debug_produce(f"in DbHandler().delete_data() error: {str(e)}")
            return result
        return result

    def drop_table(self, tableName: Union[str, TableName]) -> DataResult:
        """
        删除表,丢弃表,不是删除数据
        @param tableName:
        @return:
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            self.paramsDb[tableName].drop()
            result.state = True
        except Exception as e:
            result.info = str(e)
            debug_produce(f"in DbHandler().drop_table() error: {str(e)}")
            return result
        return result

    def get_data_list(self, tableName: Union[str, TableName], query: Dict = None,
                      projection: Dict = None, sortName: str = "", isReverse: bool = False) -> DataResult:
        """
        根据查询条件获取这个数据表的所有的数据以列表的形式返回,支持按照某个字段排序
        默认是升序,如果最后一个参数是否反转,传入true是降序
        @param tableName:表名
        @param query:查询条件
        @param projection:过滤条件,过滤的字段不存在于结果中
        @param sortName:排序的字段名称
        @param isReverse:是否反转
        @return:
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            if query is None:
                query = {}
            if projection is None:
                projection = {
                    "_id": 0,
                    "time": 0
                }
            if sortName == "":
                findData = list(self.paramsDb[tableName].find(query, projection))
            else:
                direction = -1 if isReverse else 1
                findData = list(self.paramsDb[tableName].find(query, projection)
                                .sort(sortName, direction))
            result.state = True
            result.data = findData
        except Exception as e:
            result.info = str(e)
            debug_produce(f"in DbHandler().get_data_list() error: {str(e)}")
            return result
        return result

    def get_data_one(self, tableName: Union[str, TableName], query: Dict = None,
                     projection: Dict = None, sortName: str = "", isReverse: bool = False) -> DataResult:
        """
        根据数据库获取一条数据,返回的是单条数据的字典格式,不是列表格式,如果数据为空,也返回False,表示
        无数据
        @param tableName:表名
        @param query: 查询条件
        @param projection: 过滤条件,不返回的字段
        @param sortName: 排序字段名称
        @param isReverse: 是否进行翻转,默认正序,是否逆序
        @return:
        """
        result = DataResult()
        if isinstance(tableName, TableName):
            tableName = tableName.name
        try:
            if projection is None:
                projection = {
                    "_id": 0,
                    "time": 0
                }
            if query is None:
                query = {}
            if sortName == "":
                findData = list(self.paramsDb[tableName].find(query, projection))
            else:
                direction = -1 if isReverse else 1
                findData = list(self.paramsDb[tableName].find(query, projection)
                                .sort(sortName, direction))
            if len(findData) == 0:
                result.info = f"表 {tableName} 根据查找条件 {query} 查找的数据为空"
            else:
                result.data = findData[0]
                result.state = True
        except Exception as e:
            result.info = str(e)
            result.data = {}
            debug_produce(f"in DbHandler().get_data_one() error: {str(e)}")
            return result
        return result

    def init_data(self, tableName: Union[str, TableName],
                  initData: Union[Dict, List[Dict]]) -> DataResult:
        """
        根据表名和数据初始化表,如果表不存在就创建,存在就忽略
        :param tableName:
        :param initData:
        :return:
        """

        result = DataResult()
        try:
            if tableName not in self.paramsDb.list_collection_names():
                return self.insert_data(tableName, initData)
        except Exception as e:
            errInfo = f"in  init_data() error: {e}"
            result.errInfo = errInfo
            return result

    def re_init_data(self, tableName: Union[str, TableName],
                     initData: Union[Dict, List[Dict]]) -> DataResult:
        """
        重新初始化数据表,删除旧表内容,根据initData重新创建新的表
        :param tableName:
        :param initData:
        :return:
        """
        result = DataResult()
        try:
            self.drop_table(tableName)
            return self.init_data(tableName, initData)
        except Exception as e:
            errInfo = f"in  re_init_data() error: {e}"
            result.errInfo = errInfo
            return result

    @error_handler_produce
    def get_order_show_from_web(self):
        """
        获取订单数据,用于前端展示
        :return:
        """
        from web_tool.produce_data_model import ProduceDataType
        from web_tool.send_to_web_handler import update_produce_model_in_web
        update_produce_model_in_web(ProduceDataType.workingOrder)
        update_produce_model_in_web(ProduceDataType.totalOrders)

    @error_handler_produce
    def query_history_from_web(self) -> DataResult:
        """
        获取历史记录
        :return:
        """
        from web_tool.produce_data_model import ProduceDataType
        from web_tool.send_to_web_handler import update_produce_model_in_web
        update_produce_model_in_web(ProduceDataType.totalOrders)
        return DataResult(state=True)

    @error_handler_produce
    def show_working_order_from_web(self):
        """
        获取作业工单
        :return:
        """
        from web_tool.produce_data_model import ProduceDataType
        from web_tool.send_to_web_handler import update_produce_model_in_web
        update_produce_model_in_web(ProduceDataType.workingOrder)
        return DataResult(state=True)

    @error_handler_produce
    def search_order_from_web(self, orderId: str):
        """
        搜索工单,返回工单数据,是个列表,可能有多条,也可能为空
        :param orderId:
        :return:
        """
        searchResult = self.get_data_list(TableName.orders, {"orderId": orderId})
        if searchResult.state and len(searchResult.data) > 0:
            return DataResult(state=True, data=searchResult.data)
        else:
            return DataResult(state=False, info=searchResult.info)

    def get_camera_ips(self) -> DataResult:
        """
        获取相机的IP地址
        :return:
        """
        result = self.get_data_one(TableName.cameraIps, query={"key": "1"})
        if not result.state:
            return result
        data = result.data
        leftCamera = data.get("leftCam")
        rightCamera = data.get("rightCam")
        leftIp = (leftCamera.get("cameraIP"), leftCamera.get("netIP"))
        rightIp = (rightCamera.get("cameraIP"), rightCamera.get("netIP"))
        result.data = (leftIp, rightIp)
        return result

    def delete_alarm_recors_by_keys(self, keys):
        try:
            for key in keys:
                query = {
                    "key": key
                }
                self.delete_data(TableName.alarmRecords, query)
        except Exception as e:
            print(f"in delete_alarm_recors_by_keys() error: {e}")

    def get_alarm_records_from_web(self) -> DataResult:
        return self.get_data_list(TableName.alarmRecords, sortName="happenTime", isReverse=True)

    def save_alarm_record(self, alarmContent):
        """
        保存报警记录,如果与最近10条记录中的某条时间间隔小于60秒且内容相同,则更新该记录

        Args:
            alarmContent: 报警内容
        Returns:
            DataResult: 保存结果
        """
        try:
            # 获取当前时间
            currentTime = get_now_str()

            # 获取最近10条报警记录
            dataRes = self.get_alarm_records_from_web()
            if dataRes.state:
                recentAlarms = dataRes.data[:10]
            else:
                recentAlarms = []

            # 遍历最近的报警记录
            for alarm in recentAlarms:
                # 如果报警内容相同
                if alarm["alarmContent"] == alarmContent:
                    # 计算时间差
                    lastTime = datetime.strptime(alarm["happenTime"], "%Y-%m-%d %H:%M:%S")
                    currentDateTime = datetime.strptime(currentTime, "%Y-%m-%d %H:%M:%S")
                    timeDiff = (currentDateTime - lastTime).total_seconds()

                    # 如果时间差小于60秒
                    if timeDiff < 60:
                        # 删除旧记录
                        self.delete_alarm_recors_by_keys([alarm["key"]])
                        break

            # 插入新记录
            newAlarm = {
                "key": time.time(),
                "happenTime": currentTime,
                "alarmContent": alarmContent
            }
            self.insert_data(TableName.alarmRecords, newAlarm)
            # 更新到前端界面上去
            from web_tool.send_to_web_handler import update_alarm_records_in_web
            update_alarm_records_in_web()
            return True

        except Exception as e:
            errorInfo = f"in DbHandler.save_alarm_record(), error: {str(e)}"
            debug_produce(errorInfo)
            return False

    @error_handler_produce
    def delete_current_page_from_web(self, keys) -> DataResult:
        """
        删除当前页的报警信息
        :param self:
        :param keys:
        :return:
        """
        for key in keys:
            self.delete_data(TableName.alarmRecords, {"key": key})
        return DataResult(state=True)

    @error_handler_produce
    def delete_total_page_from_web(self) -> DataResult:
        """
        删除所有的数据
        :return:
        """
        self.delete_data(TableName.alarmRecords, query={})
        return DataResult(state=True)

    @error_handler_produce
    def get_produce_amount_data(self) -> DataResult:
        dataRes = self.get_data_one(TableName.produceAmountRecords, {"key": "1"})
        if not dataRes.state:
            return dataRes
        data = dataRes.data
        recordList = [data.get(str(i)) for i in range(1, 13)]
        webShowDataList = [(str((index + 1)) + "月", data) for index, data in enumerate(recordList)]

        dayAmountDict = data.get("dayAmount")
        newDayAmountTableVal = []
        tableDataIndex1 = "dayIndex"
        tableDataIndex2 = "dayAmount"
        dayAmountDictKey = dayAmountDict.keys()
        newAmountDictKey = sorted(dayAmountDictKey, reverse=True)
        nowDay = get_now_str()[5:10]
        if len(newAmountDictKey) > 31:  # 如果超过了31天
            nowDayIndex = newAmountDictKey.index(nowDay)
            newAmountDictKey = newAmountDictKey[nowDayIndex:]
        if len(newAmountDictKey) >= 31:
            newAmountDictKey = newAmountDictKey[:32]

        newAmountDict = {key: dayAmountDict[key] for key in newAmountDictKey}

        for key, val in newAmountDict.items():
            newVal = {
                "key": key,
                tableDataIndex1: key,
                tableDataIndex2: f"早班:  {val[0]}   |  晚班:  {val[1]}  | 总计:  {val[2]}"
            }
            newDayAmountTableVal.append(newVal)

        result = DataResult()
        result.state = True
        result.data = [webShowDataList, newDayAmountTableVal]
        return result

    def update_produce_record(self, boardNumbers) -> DataResult:
        """
        更新产量记录里面的值,月份和日期都要来一遍
        :param boardNumbers: 增加的板的数量.
        :return:
        """
        query = {"key": "1"}
        # 先获取produceAmount数据
        dataRes = self.get_data_one(TableName.produceAmountRecords, query)
        if not dataRes.state:
            return dataRes
        data = dataRes.data
        nowYear = datetime.now().year
        recordYear = data.get("recordYear")
        if nowYear != recordYear:  # 如果记录的年份变了,数据就要重新清零
            self.delete_data(TableName.produceAmountRecords, query)
            newData = {
                "key": "1",
                "recordYear": datetime.now().year,
                "1": 0,
                "2": 0,
                "3": 0,
                "4": 0,
                "5": 0,
                "6": 0,
                "7": 0,
                "8": 0,
                "9": 0,
                "10": 0,
                "11": 0,
                "12": 0,
                "dayAmount": {

                }
            }
            self.insert_data(TableName.produceAmountRecords, newData)

            dataRes = self.get_data_one(TableName.produceAmountRecords, query)
            if not dataRes.state:
                return dataRes
            data = dataRes.data
        monthKey = str(datetime.now().month)
        shift, dateKey = get_shift_period()  # 获取早班还是晚班
        print("monthKey = {},dateKey = {}".format(monthKey, dateKey))
        oldMonthVal = data.get(monthKey, 0)
        oldDayAmountDict = data.get("dayAmount", {})
        oldDayVal = oldDayAmountDict.get(dateKey, [0, 0, 0])  # 是个列表 [早班数量,晚班数量,总数量]
        newMonthVal = oldMonthVal + boardNumbers
        if shift == "早班":
            newDateVal = [oldDayVal[0] + boardNumbers, oldDayVal[1], oldDayVal[2] + boardNumbers]
        else:
            newDateVal = [oldDayVal[0], oldDayVal[1] + boardNumbers, oldDayVal[2] + boardNumbers]
        oldDayAmountDict[dateKey] = newDateVal

        newData = {
            monthKey: newMonthVal,
            "dayAmount": oldDayAmountDict,
        }
        return self.update_data(TableName.produceAmountRecords, query, newData)

    def delete_order_from_web(self, deleteKey) -> DataResult:
        """
        根据工单的key将工单删除
        """
        query = {"key": deleteKey}
        return self.delete_data(TableName.orders, query)

    def get_total_order_show_from_web(self) -> DataResult:
        """
        获取所有的工单信息
        """
        from web_tool.produce_data_model import ProduceDataType
        from web_tool.send_to_web_handler import update_produce_model_in_web
        update_produce_model_in_web(ProduceDataType.totalOrders)
        return DataResult()


if __name__ == '__main__':
    db = DbHandler()
    # 报警测试
    # for i in range(100):
    #     db.save_alarm_record("测试{}".format(i))
    #     gevent.sleep(0.01)

    # 产量测试
    result = db.update_produce_record(10)
    print(result.state, result.info)
